Saltar para o conteúdo

Despacho múltiplo

Origem: Wikipédia, a enciclopédia livre.

Despacho múltiplo (ou multimétodos) é a característica de algumas linguagens orientadas a objeto em que uma função ou método pode ser lançado dinamicamente baseado no tipo de tempo de execução (dinâmico) de mais de um de seus argumentos. Isto é uma extensão do polimorfismo de despacho único onde uma chamada de método é despachada dinamicamente baseada no tipo derivado do objeto atual sobre o qual o método foi chamado. O despacho múltiplo generaliza o despacho dinâmico para trabalhar com uma combinação de dois ou mais objetos.

Entendendo o despacho

[editar | editar código-fonte]

Desenvolvedores de software de computador normalmente organizam o código fonte em blocos chamados variadamente de subrotinas, procedimentos, subprogramas, funções ou métodos. O código na função é executado pela sua chamada - executando um pedaço de código que referencia seu nome. Isto transfere o controle temporariamente para a função chamada. Quando a execução da função é concluída, o controle normalmente é devolvido para a instrução no chamador que segue a referência.

Nomes de função normalmente são escolhidos de forma a serem descritivos do propósito da função. Algumas vezes é desejável fornecer a várias funções o mesmo nome, frequentemente porque elas realizam, conceitualmente, tarefas similares, mas operam sobre tipos de dados de entrada diferentes. Em tais casos, a referência de nome no local da chamada da função não é suficiente para identificar o bloco de código a ser executado. Em vez disso, o número e o tipo dos argumentos para a chamada da função também são usados para selecionar entre várias implementações da função.

Nas linguagens de programação orientadas a objeto "convencionais", isto é, de despacho simples, quando invoca-se um método ("enviando uma mensagem" em Smalltalk, "chamando uma função membro" em C++), um de seus argumentos é tratado de maneira especial e usado para determinar qual dos (potencialmente muitos) métodos com aquele nome deve ser aplicado. Em muitas linguagens, o argumento "especial" é indicado sintaticamente. Por exemplo, um número de linguagens de programação colocam o argumento especial antes de um ponto ao fazer uma chamada de método: especial.metodo(outros, argumentos, aqui), de modo que leao.chamada() produziria um rugido, pardal.chamada() produziria um pio.

Em contraste, em linguagens com despacho múltiplo, o método escolhido é simplesmente aquele cujos argumentos correspondem com o número e tipo da chamada da função. Não há argumento "especial" que "possui" a função/método executado em uma chamada particular.

O Common Lisp Object System (CLOS) é um antigo e bem conhecido exemplo de despacho múltiplo.

Tipos de dados

[editar | editar código-fonte]

Quando trabalha-se com linguagens que discriminam os tipos de dados em tempo de compilação, a seleção entre as alternativas pode ocorrer em tempo de compilação. A criação de tais funções alternativas em tempo de compilação é geralmente referido como sobrecarga.

Em linguagens de programação que discriminam os tipos de dados em tempo de execução, a seleção entre as alternativas pode ocorrer em tempo de execução, baseado nos tipos determinados dinamicamente dos argumentos. Funções cujas implementações alternativas são selecionadas dessa forma são chamadas multimétodos.

Há um custo computacional associado às chamadas de função dinamicamente despachadas.

Uso em diferentes linguagens

[editar | editar código-fonte]

Em uma linguagem com despacho único como Java, o código pode ser estruturado da seguinte forma:

 abstract class Coisa {
     /** tornando este método não abstrato não mudaria a demonstração */
     public abstract void colideCom(Coisa outra);
 }
 
 class Asteroide extends Coisa {
     public void colideCom(Coisa outra) {
         if (outra instanceof Asteroide) {
             // trata colisão Asteroide-Asteroide
         }
         else if (outra instanceof Espaconave) {
             // trata colisão Asteroide-Espaconave
         }
     }
 }
 
 class Espaconave extends Coisa {
     public void colideCom(Coisa outra) {
         if (outra instanceof Asteroide) {
             // trata colisão Espaconave-Asteroide
         }
         else if (outra instanceof Espaconave) {
             // trata colisão Espaconave-Espaconave
         }
     }
 }

Em uma linguagem com despacho múltiplo como Common Lisp, o código pode ser estruturado da seguinte forma:

 (defmethod colide-com ((x asteroide) (y asteroide))
   ;; trata colisão Asteroide-Asteroide
   )
 (defmethod colide-com ((x asteroide) (y espaconave))
   ;; trata colisão Asteroide-Espaconave
   )
 (defmethod colide-com ((x espaconave) (y asteroide))
   ;; trata colisão Espaconave-Asteroide
   )
 (defmethod colide-com ((x espaconave) (y espaconave))
   ;; trata colisão Espaconave-Espaconave
   )

Note que o teste explícito e a conversão de tipo não é usada.

O despache múltiplo difere da sobrecarga de métodos em C++ já que é feito em tempo de execução a partir dos tipos dinâmicos dos argumentos, ao invés de em tempo de compilação a partir dos tipos estáticos dos argumentos. Autor do C++, Bjarne Stroustrup discutiu o tema e propôs alternativas para o C++[1].

Em linguagens que não suportam despacho múltiplo na definição da linguagem ou a nível sintático, é geralmente possível adicionar a funcionalidade através de uma extensão da biblioteca. por exemplo, o módulo multimethods.py do Python prove a funcionalidade ao estilo CLOS sem a mudança da sintaxe da linguagem.

from multimethods import Dispatch
from objetos_do_jogo import Asteroide, Espaconave
from comportamentos_do_jogo import AEFunc, EEFunc, EAFunc, AAFunc
colide = Dispatch()
colide.add_rule((Asteroide, Espaconave), AEFunc)
colide.add_rule((Espaconave, Espaconave), EEFunc)
colide.add_rule((Espaconave, Asteroide), EAFunc)
colide.add_rule((Asteroide, Asteroide), AAFunc)

# ...posteriormente...
colide(objeto1, objeto2)

Funcionalmente, isto é muito similar ao exemplo CLOS, mas a sintaxe é do Python convencional.

Usando decoradores do Python 2.4, Guido van Rossum produziu uma implementação modelo de multimétodos[2] com uma sintaxe simplificada:

@multimethod(Asteroide, Asteroide)
def colide(a, b):
    """Comportamento para colisão asteroide e asteroide"""
    # ...define...
@multimethod(Asteroide, Espaconave)
def colide(a, b):
    """comportamento para colisão asteroide e espaconave"""
    # ...define...

Julia é uma linguagem dinâmica orientada para programação numérica, estruturada sobre despachos múltiplos desde sua concepção. Sua sintaxe é simples e legível e, no exemplo acima, o código seria estruturado como:

function colide_com( x :: Asteroide, y :: Asteroide )
    # trata colisão Asteroide-Asteroide
end
function colide_com( x :: Asteroide, y :: Espaconave )
   # trata colisão Asteroide-Espaconave
end
function colide_com( x :: Espaconave, y :: Asteroide )
   # trata colisão Espaconave-Asteroide
end
function colide_com( x :: Espaconave, y :: Espaconave )
   # trata colisão Espaconave-Espaconavee
end

Referências

  1. Peter Pirkelbauer, Yuriy Solodkyy, Bjarne Stroustrup (Outubro de 2007). «Open Multi-Methods for C++» (PDF). Proceedings of the 6th international conference on Generative programming and component engineering, 2007 (em inglês). Sítio pessoal de Bjarne Stroustrup. Consultado em 25 de junho de 2008. Arquivado do original (PDF) em 14 de maio de 2008 
  2. «multimethods.py»  Multiple dispatch in Python with configurable dispatch resolution by David Mertz, et al.